package com.hmdp.service.impl;


import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.BloomFilterInitializer;
import com.hmdp.utils.CacheClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import static com.hmdp.utils.RedisConstants.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    //创建一个线程池
//    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private ShopMapper shopMapper;
    @Resource
    private CacheClient cacheClient;
    @Resource
    private BloomFilterInitializer bloomFilterInitializer;
    @Override
    public Result queryById(Long id) {
        //缓存穿透
        // Shop shop = queryWithPassThrough(id);

        //互斥锁解决缓存击穿
        //Shop shop = queryWithPassMutex(id);

        //逻辑过期解决缓存击穿
        Shop shop = cacheClient.queryWithPassThrough(
                CACHE_SHOP_KEY, id, Shop.class, this::getById, CACHE_SHOP_TTL, TimeUnit.MINUTES);
        if (shop == null) {
            return Result.fail("店铺不存在");
        }
        return Result.ok(shop);
    }

//逻辑过期解决缓存击穿
//    public Shop queryWithLogicalExpire(Long id){
//        String key = CACHE_SHOP_KEY + id;
//        String shopJson = stringRedisTemplate.opsForValue().get(key);
//        if(StrUtil.isBlank(shopJson)){
//            return null;
//        }
//        //命中，需要先把json反序列为对象
//        RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
//        Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);
//        LocalDateTime expireTime = redisData.getExpireTime();
//        //判断是否过期
//        if(expireTime.isAfter(LocalDateTime.now())){
//            //未过期直接返回店铺信息
//            return shop;
//        }
//       //已过期需求缓存重建
//        //尝试获取互斥锁
//        String lockKey = LOCK_SHOP_KEY + id;
//        boolean flag = tryLock(lockKey);
//        //判断是否获取锁
//        if(flag){
//            CACHE_REBUILD_EXECUTOR.submit(() ->{
//                //重建缓存
//                try {
//                    this.saveShop2Redis(id,20L);
//                }catch (Exception e){
//                    throw new RuntimeException(e);
//                }finally {
//                    unLock(lockKey);
//                }
//            });
//        }
//        //失败，返回店铺信息
//        return shop;
//    }

    @Override
    public Result update(Shop shop) {
        if (shop.getId() == null) {
            return Result.fail("店铺id不能为空");
        }
        shopMapper.updateById(shop);
        stringRedisTemplate.delete(CACHE_SHOP_KEY + shop.getId());
        return Result.ok();
    }
//互斥锁解决缓存击穿
//    public Shop queryWithPassMutex(Long id){
//        String key = CACHE_SHOP_KEY + id;
//        String shopJson = stringRedisTemplate.opsForValue().get(key);
//        if(StrUtil.isNotBlank(shopJson)){
//            return JSONUtil.toBean(shopJson,Shop.class);
//        }
//        if(shopJson !=null){
//            return null;
//        }
//        //实现缓存从建
//        //获取互斥锁
//        String lockKey = LOCK_SHOP_KEY + id;
//        Shop shop = null;
//     try {
//         Boolean flag = tryLock(lockKey);
//         //判断是否获取锁
//         if(!flag){
//             //失败，休眠重试
//             Thread.sleep(50);
//             queryWithPassThrough(id);
//         }
//         //成功，根究id查询数据库
//         // TODO :获取锁成功应该再次检测redis缓存是否过期，做DoubleCheck,如果存在则无需重建缓存
//         shop = getById(id);
//         if(shop ==null){
//             stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
//             return null;
//         }
//         //存在，写入redis
//         stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
//     }catch (Exception e){
//         throw new RuntimeException(e);
//     }finally {
//         //释放互斥锁
//         unLock(key);
//     }
//        return shop;
//    }


//设置空值缓存穿透
//    public Shop queryWithPassThrough(Long id){
//        String key = CACHE_SHOP_KEY + id;
//        String shopJson = stringRedisTemplate.opsForValue().get(key);
//        if(StrUtil.isNotBlank(shopJson)){
//            return JSONUtil.toBean(shopJson,Shop.class);
//        }
//        if(shopJson !=null){
//            return null;
//        }
//        Shop shop = getById(id);
//        if(shop ==null){
//            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
//            return null;
//        }
//        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
//        return shop;
//    }
//
//    public void saveShop2Redis(Long id,Long expireSeconds){
//        Shop shop = getById(id);
//        RedisData redisData = new RedisData();
//        redisData.setData(shop);
//        redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
//        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));
//    }


//获取锁
//    private Boolean tryLock(String key){
//        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
//        return BooleanUtil.isTrue(flag);
//    }


    //释放锁
//    private void unLock(String key){
//      stringRedisTemplate.delete(key);
//    }

    // 缓存穿透解决方案：布隆过滤器 + 缓存空对象（双重保障）
    public Shop queryWithBloomFilter(Long id){
        //布隆过滤器校验
        if(!bloomFilterInitializer.mightContain(id)){
            //一定不存在，返回null
            return null;
        }
        //查询缓存
        String key=CACHE_SHOP_KEY+id;
        String shopJson=stringRedisTemplate.opsForValue().get(key);
        //缓存命中
        if(StrUtil.isNotBlank(shopJson)){
            return JSONUtil.toBean(shopJson,Shop.class);
        }

        //缓存命中空值
        if(shopJson!=null){
            return null;
        }
        Shop shop=getById(id);
        // 步骤6：数据库不存在该商品
        if (shop== null) {
            // 缓存空对象（进一步防止穿透，应对布隆过滤器误判）
            stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }
        // 步骤7：数据库存在，写入缓存并返回
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return shop;
    }
}
